{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img align=\"right\" style=\"max-width: 200px; height: auto\" src=\"cfds_logo.png\">\n",
    "\n",
    "###  Lab 04 - \"Mean Reversion Trading Strategies\"\n",
    "\n",
    "Chartered Financial Data Scientist (CFDS), Spring Term 2020"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this introductory lab, we create our first **financial data science process**. The main objective of this lab is to walk you through the general process of implementing and evaluating a simple **mean-reversion** trading strategy. To achieve this, we will follow the distinct process steps as outlined below:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img align=\"middle\" style=\"max-width: 600px; height: auto\" src=\"fds_process.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As always, pls. don't hesitate to ask all your questions either during the lab or send us an email (using our\n",
    "fds.ai email addresses)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Lab Objectives:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After today's lab you should be able to:\n",
    "    \n",
    "> 1. Implement a **mean-reversion trading strategy** and apply it to distinct financial instruments.\n",
    "> 2. Convert the trading strategy results into **trade signals** to be used in backtest.\n",
    "> 3. Understand how to use the **python backtesting bt** library to backtest the implemented strategy.\n",
    "> 4. Interpret the backtests results using the distinct **backtest performance** measures."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before we start let's watch a motivational video:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import YouTubeVideo\n",
    "# Nvidia GTC 2017: \"I Am AI\" Opening in Keynote\"\n",
    "# YouTubeVideo('SUNPrR4o5ZA', width=800, height=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup of the Analysis Environment"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We need to import a couple of Python libraries that allow for data analysis and data visualization. In this lab will use the `Pandas`, `NumPy`, `BT` and the `Matplotlib` library. Let's import the libraries by the execution of the statements below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# import python utility libraries\n",
    "import os as os\n",
    "import datetime as dt\n",
    "import itertools as it\n",
    "\n",
    "# import python data science libraries\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "# import the pandas financial data reader library\n",
    "import pandas_datareader as dr\n",
    "\n",
    "# import the Python bt backtest library\n",
    "import bt as bt\n",
    "\n",
    "# import the matplotlib and seaborn visualization library\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's also set a couple of general plot parameters:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# set general plot parameters\n",
    "plt.style.use('seaborn')\n",
    "plt.rcParams['figure.figsize'] = [10, 5]\n",
    "plt.rcParams['figure.dpi']= 150"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Enable inline Jupyter notebook plotting:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Suppress potential warnings due to recent library enhancements:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings('ignore')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a **dataset** sub-folder that we will use to store the financial data downloaded:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not os.path.exists('./datasets'): os.makedirs('./datasets')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Acquire the Financial Data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this section of the lab notebook, we will aquire historic daily stock market data of the **Euro vs. US-Dollar** foreign exchange rate (ticker symbol: \"EURUSD\"). Thereby, we will utilize the `datareader` of the `Pandas` library that provides the ability to interface the `Yahoo` finance API. Let's first specify the start date and end date of the data download. We aim to download the exchange rate data starting from the **31.12.2003** until the **31.12.2017** to develop and evaluate a simple mean-reversion trading strategy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# set to start and end date of the data download\n",
    "start_date = dt.datetime(2003, 12, 31)\n",
    "end_date = dt.datetime(2017, 12, 31)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Download the **daily** \"Euro vs. USD\" exchange rate data of the defined timeframe using the `datareader`'s `Yahoo` finance API:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# download eurusd exchange rate data\n",
    "eurusd_data = dr.data.DataReader('EURUSD=X', data_source='yahoo', start=start_date, end=end_date)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Pre-Process the Financial Data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inspect the top 10 records of the `EURUSD` data downloaded:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eurusd_data.head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Visually inspect the **adjusted closing price** of the downloaded `EURUSD`\n",
    " data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.rcParams['figure.figsize'] = [15, 5]\n",
    "fig = plt.figure()\n",
    "ax = fig.add_subplot(111)\n",
    "\n",
    "# plot eurusd daily adjusted closing prices\n",
    "ax.plot(eurusd_data.index, eurusd_data['Adj Close'], color='#9b59b6')\n",
    "\n",
    "# rotate x-ticks\n",
    "for tick in ax.get_xticklabels():\n",
    "    tick.set_rotation(45)\n",
    "\n",
    "# set axis labels\n",
    "ax.set_xlabel('[time]', fontsize=10)\n",
    "ax.set_xlim([start_date, end_date])\n",
    "ax.set_ylabel('[adjusted closing price]', fontsize=10)\n",
    "\n",
    "# set plot title\n",
    "plt.title('Euro vs. US-Dollar Exchange Rate - Historical Prices', fontsize=10);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Save the downloaded `EURUSD` data to the local directory:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eurusd_data.to_csv('./datasets/eurusd_data_2003_2017_daily.csv', sep=';', encoding='utf-8')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Data Analysis - Mean Reversion Strategy Implementation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's implement a simple **Mean Reversion** trading strategy. In general, **mean reversion trading** refers to the idea that extreme market movements are more likely followed by an \"average movement\" than by an even more extreme market movement. \n",
    "\n",
    "Mean reversion trading is derived from the observation that the price of financial instruments tend to **revert to their mean price** over time. It is assumed, that the price of a financial instrument is prone to random fluctuations around an underlying (potentially) stable trend. This behaviour can be frequently observed when analyzing price charts of foreign exchange rates such as the EUR to JPY fx-rate, as observable in the following illustration:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img align=\"middle\" style=\"max-width: 800px; height: auto\" src=\"meanreversiontrend.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the context of mean reversion trading it is aimed to trade such (tiny) fluctuations around such more stable trends. To achieve this will, we apply a technique referred to as **\"Bollinger Bands\"** proposed by John Bollinger in the 1980's. Bollinger Bands characterize the price volatility of a financial instrument over time. In general, the bands are determined by:\n",
    "\n",
    "\n",
    "$$BB^{upper}(t, n, k) = SMA(t, n) + k \\cdot \\sigma(t)$$\n",
    "\n",
    "$$BB^{lower}(t, n, k) = SMA(t, n) - k \\cdot \\sigma(t)$$\n",
    "\n",
    "\n",
    "where $t$ denotes the current point in time and the distinct elements of the Bollinger Bands calculation correspond to:\n",
    "\n",
    ">- $SMA(t, n)$ denotes a **simple moving average** with a lookback window of $n$ historical prices $p_i$ defined by $SMA(t, n)=\\frac{1}{n} \\sum_{k=0}^{n-1} p_{i}(t-k)$, e.g. a 20, 50, or 100-days moving average lookback window; \n",
    ">- $BB^{upper}(t, n, k)$ denotes the **upper Bollinger Band** defined by adding $k$-times the positive standard deviation $\\sigma_i$ of the $n$ historical prices $p_i$ to the simple moving average $SMA(t, n)$; and,\n",
    ">- $BB^{lower}(t, n, k)$ denotes the **lower Bollinger Band** defined by subtracting $k$-times the positive standard deviation $\\sigma_i$ of the $n$ historical prices $p_i$ from the simple moving average $SMA(t, n)$.\n",
    "\n",
    "The following illustration shows the calculated Bollinger Bands $BB^{upper}$ and $BB^{lower}$ at distinct timesteps $t$ and different $k$ parametrizations:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img align=\"middle\" style=\"max-width: 800px; height: auto\" src=\"bollingerbands.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's start inspect the Bollinger Bands of the mean-reversion trading strategy by setting the moving average window lookback size:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_lookback_days_20 = 20   # set the mean-reversion moving average indicator lookback, days = 20"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can calculate the moving (rolling) average using the Pandas `rolling()` and `mean()` function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_mav_20 = pd.Series(eurusd_data['Adj Close'].rolling(window = mre_lookback_days_20).mean(), name = 'SMAV_20')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similarly, we can calculate the moving (rolling) standard deviation $\\sigma$ using the Pandas `rolling()` and `std()` function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_std_20 = pd.Series(eurusd_data['Adj Close'].rolling(window = mre_lookback_days_20).std(), name = 'STD_20')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Merge the obtained rolling moving average and standard deviation values with the original echange rate price data (adjusted closing prices):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_mav_eurusd_data = eurusd_data.join(mre_mav_20)\n",
    "mre_mav_eurusd_data = mre_mav_eurusd_data.join(mre_std_20)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inspect and validate the daily adjusted closing prices of the EURUSD exchange rate as well as the derived (i) moving average and (ii) standard deviation values starting from the first obtained 20-day moving average price:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_mav_eurusd_data[['Adj Close', 'SMAV_20', 'STD_20']].iloc[20:30]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To gain an even more detailed intuition let's determine and visualize different degrees of rolling standard deviation obtainable from the 20-day moving average price. Obtain a rolling adjusted closing price standard deviation of $\\sigma = \\pm 1$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# one standard deviations\n",
    "mre_mav_eurusd_data['POS_STD1_20'] = mre_mav_eurusd_data['Adj Close'] + (1.0 * mre_mav_eurusd_data['STD_20'])\n",
    "mre_mav_eurusd_data['NEG_STD1_20'] = mre_mav_eurusd_data['Adj Close'] - (1.0 * mre_mav_eurusd_data['STD_20'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similarly, obtain a rolling adjusted closing price standard deviation of $\\sigma = \\pm 2$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# two standard deviations\n",
    "mre_mav_eurusd_data['POS_STD2_20'] = mre_mav_eurusd_data['Adj Close'] + (2.0 * mre_mav_eurusd_data['STD_20'])\n",
    "mre_mav_eurusd_data['NEG_STD2_20'] = mre_mav_eurusd_data['Adj Close'] - (2.0 * mre_mav_eurusd_data['STD_20'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And finally, obtain a rolling adjusted closing price standard deviation of $\\sigma = \\pm 3$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# three standard deviations\n",
    "mre_mav_eurusd_data['POS_STD3_20'] = mre_mav_eurusd_data['Adj Close'] + (3.0 * mre_mav_eurusd_data['STD_20'])\n",
    "mre_mav_eurusd_data['NEG_STD3_20'] = mre_mav_eurusd_data['Adj Close'] - (3.0 * mre_mav_eurusd_data['STD_20'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inspect and validate the daily adjusted closing prices of the EURUSD exchange rate as well the different degrees of deviating standard deviations starting from the first obtained 20-day moving average price:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_mav_eurusd_data[['Adj Close', 'SMAV_20', 'STD_20', 'POS_STD1_20', 'NEG_STD1_20', 'POS_STD2_20', 'NEG_STD2_20', 'POS_STD3_20', 'NEG_STD3_20']].iloc[20:30]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Plot the historical daily adjusted closing prices of the EUR vs. US-Dollar (blue) as well as its rolling 20 days standard deviations of $\\sigma=1$ standard deviations (top), $\\sigma=2$ standard deviations (middle) as well as $\\sigma=3$ standard deviations (bottom):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.rcParams['figure.figsize'] = [15, 15]\n",
    "fig, ax = plt.subplots(ncols=1, nrows=3)\n",
    "\n",
    "#### plot the standard deviation of 1 \n",
    "\n",
    "# plot moving average adjusted closing price standard deviation of 1\n",
    "ax[0].fill_between(mre_mav_eurusd_data.index, mre_mav_eurusd_data['POS_STD1_20'], mre_mav_eurusd_data['NEG_STD1_20'], color='C2', lw=2.0, label='$Stdv. \\sigma = 1$ (red)', alpha=0.3)\n",
    "\n",
    "# plot adjusted closing price \n",
    "ax[0].plot(mre_mav_eurusd_data['Adj Close'], lw=1.0, color='C3', label='Adj. Closing Prices (purple)')\n",
    "\n",
    "# rotate x-tick labels\n",
    "for tick in ax[0].get_xticklabels():\n",
    "    tick.set_rotation(45)\n",
    "\n",
    "# set axis labels\n",
    "ax[0].set_xlabel('[time]', fontsize=10)\n",
    "ax[0].set_xlim([start_date, end_date])\n",
    "ax[0].set_ylabel('[market price]', fontsize=10)\n",
    "\n",
    "# set plot legend\n",
    "ax[0].legend(loc=\"upper left\", numpoints=1, fancybox=True)\n",
    "\n",
    "#### plot the standard deviation of 2 \n",
    "\n",
    "# plot moving average adjusted closing price standard deviation of 2\n",
    "ax[1].fill_between(mre_mav_eurusd_data.index, mre_mav_eurusd_data['POS_STD2_20'], mre_mav_eurusd_data['NEG_STD2_20'], color='C2', lw=2.0, label='$Stdv. \\sigma = 2$ (red)', alpha=0.3)\n",
    "\n",
    "# plot adjusted closing price \n",
    "ax[1].plot(mre_mav_eurusd_data['Adj Close'], lw=1.0, color='C3', label='Adj. Closing Prices (purple)')\n",
    "\n",
    "# rotate x-tick labels\n",
    "for tick in ax[1].get_xticklabels():\n",
    "    tick.set_rotation(45)\n",
    "\n",
    "# set axis labels\n",
    "ax[1].set_xlabel('[time]', fontsize=10)\n",
    "ax[1].set_xlim([start_date, end_date])\n",
    "ax[1].set_ylabel('[market price]', fontsize=10)\n",
    "\n",
    "# set plot legend\n",
    "ax[1].legend(loc=\"upper left\", numpoints=1, fancybox=True)\n",
    "\n",
    "#### plot the standard deviation of 3 \n",
    "\n",
    "# plot moving average adjusted closing price standard deviation of 3\n",
    "ax[2].fill_between(mre_mav_eurusd_data.index, mre_mav_eurusd_data['POS_STD3_20'], mre_mav_eurusd_data['NEG_STD3_20'], color='C2', lw=2.0, label='$Stdv. \\sigma = 3$ (red)', alpha=0.3)\n",
    "\n",
    "# plot adjusted closing price \n",
    "ax[2].plot(mre_mav_eurusd_data['Adj Close'], lw=1.0, color='C3', label='Adj. Closing Prices (purple)')\n",
    "\n",
    "# rotate x-tick labels\n",
    "for tick in ax[2].get_xticklabels():\n",
    "    tick.set_rotation(45)\n",
    "\n",
    "# set axis labels\n",
    "ax[2].set_xlabel('[time]', fontsize=10)\n",
    "ax[2].set_xlim([start_date, end_date])\n",
    "ax[2].set_ylabel('[market price]', fontsize=10)\n",
    "\n",
    "# set plot legend\n",
    "ax[2].legend(loc=\"upper left\", numpoints=1, fancybox=True)\n",
    "\n",
    "# set plot title\n",
    "ax[0].set_title('Euro vs. US-Dollar Exchange Rate - Historical Prices, $\\sigma=1$', fontsize=10)\n",
    "ax[1].set_title('Euro vs. US-Dollar Exchange Rate - Historical Prices, $\\sigma=2$', fontsize=10)\n",
    "ax[2].set_title('Euro vs. US-Dollar Exchange Rate - Historical Prices, $\\sigma=3$', fontsize=10)\n",
    "\n",
    "# reset plot layout\n",
    "plt.tight_layout()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Mean Reversion Trading Signal Generation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will make use of the **\"Standard-Score\"** or **\"Z-Score\"** to convert the Bollinger Band information into a series of binary long- and short-trading-signals of a mean reversion trading strategy. The **\"Z-Score\"** is the signed number of standard deviations by which the actual price $p_{i}(t)$ of a financial instrument $i$ falls above or below the moving average price, formally denoted by:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$ z_{i}(t) = \\frac{p_{i}(t)-SMA_{i}(t,n)}{\\sigma_{i}(t, n)}$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "where $t$ denotes the current point in time and the distinct elements of the Z-Score are defined by: \n",
    "\n",
    ">- $SMA(t, n)$ denotes a **simple moving average** with a lookback window of $n$ historical prices $p_i$ defined by $SMA(t, n)=\\frac{1}{n} \\sum_{k=0}^{n-1} p_{i}(t-k)$, e.g. a 20, 50, or 100-days moving average lookback window; and,\n",
    ">- $\\sigma_{i}(t, n)$ denotes the **moving average strandard deviation** with a lookback window of $n$ historical prices $p_i$, e.g. a 20, 50, or 100-days moving average lookback window."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's now determine the Z-Score at distinct time steps of the EUR vs. US-Dollar foreign exchange rate: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_mav_eurusd_data['Z_SCORE'] = (mre_mav_eurusd_data['Adj Close'] - mre_mav_eurusd_data['SMAV_20']) / mre_mav_eurusd_data['STD_20']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inspect and validate the different rolling Z scores obtained, starting from the first obtained 20-day moving average price:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_mav_eurusd_data[['Adj Close', 'SMAV_20', 'STD_20', 'Z_SCORE']].iloc[20:30]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's now derive a mean-reversion trading signal from the calculated rolling Z-Score of the EUR vs. US-Dollar foreign exchange rate. In order to derive such a signal we first specify an upper Z-Score threshold $\\alpha$ and a lower Z-Score threshold $\\beta$, where $\\alpha > \\beta$. Afterwards, we are able to derive a mean-reversion trading signal according to the following rules:\n",
    "\n",
    ">- **\"Long-signal\"** (+1.0) signal if $z_{i}(t) \\leq -\\; \\alpha \\cdot z_{i}(t)$;\n",
    ">- **\"Close Long-signal\"** (0.0) signal if $z_{i}(t) \\leq -\\; \\beta \\cdot z_{i}(t)$;\n",
    ">- **\"Short-signal\"** (+1.0) signal if $z_{i}(t) \\geq +\\; \\alpha \\cdot z_{i}(t)$; and,\n",
    ">- **\"Close Short-signal\"** (0.0) signal if $z_{i}(t) \\geq +\\; \\beta \\cdot z_{i}(t)$.\n",
    "\n",
    "Let's now start to determine the mean-reversion trading signals by setting the Z-Score thresholds. Thereby, we will set both Z-Score thresholds $\\alpha = 1.0$ and $\\beta = 0.5$ respectively, as done in the following:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z_score_alpha_threshold = 1.0\n",
    "z_score_beta_threshold = 0.5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Subsequently we implement and derive the mean-reversion trading signals of the EUR vs. US-Dollar foreign exchange rate using both Z-Score thresholds as defined above:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# determine the distinct z-scores\n",
    "z_scores = mre_mav_eurusd_data['Z_SCORE']\n",
    "\n",
    "# init mean reversion signal\n",
    "mre_trade_signal = np.zeros(len(z_scores))\n",
    "\n",
    "# iterate over z-scores\n",
    "for i in range(20, len(z_scores)):\n",
    "    \n",
    "    # determine actual z-score\n",
    "    z_score = z_scores[i]\n",
    "    \n",
    "    # case: active trading signal\n",
    "    if mre_trade_signal[i-1] == 0.0:\n",
    "    \n",
    "        # case: z-score exceeds positive threshold\n",
    "        if z_score > z_score_alpha_threshold:\n",
    "\n",
    "            # set 'short' signal\n",
    "            mre_trade_signal[i] = -1.0\n",
    "\n",
    "        # case: z-score exceeds negative threshold\n",
    "        elif z_score < (z_score_alpha_threshold * -1.0):\n",
    "\n",
    "            # set 'long' signal\n",
    "            mre_trade_signal[i] = 1.0\n",
    "        \n",
    "        # case: z-score doesn't exceed thresholds\n",
    "        else:\n",
    "            \n",
    "            # keep prior signal\n",
    "            mre_trade_signal[i] = mre_trade_signal[i-1]\n",
    "    \n",
    "    # case: inactive trading signal\n",
    "    elif mre_trade_signal[i-1] != 0.0:\n",
    "        \n",
    "        # z-score reverted back to moving average\n",
    "        if abs(z_score) < z_score_beta_threshold:\n",
    "            \n",
    "            # set 'neutral' signal\n",
    "            mre_trade_signal[i] = 0.0\n",
    "        \n",
    "        # z-score not yer reverted back to moving average\n",
    "        elif abs(z_score) > z_score_beta_threshold:\n",
    "            \n",
    "            # keep prior signal\n",
    "            mre_trade_signal[i] = mre_trade_signal[i-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Convert the obtained trading signals into a Pandas DataFrame and merge it with the market price data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# convert signals to Pandas DataFrame\n",
    "mre_mav_eurusd_data_signal = pd.DataFrame(mre_trade_signal, columns=['SIGNAL_20'], index=mre_mav_eurusd_data.index)\n",
    "\n",
    "# convert pandas DataFrame index to datatype: datetime\n",
    "mre_mav_eurusd_data['SIGNAL_20'] = mre_mav_eurusd_data_signal"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inspect and validate the different Z scores and **mean-reversion trading strategy signals** obtained, starting from the first obtained 20-day moving average price:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_mav_eurusd_data[['Adj Close', 'SMAV_20', 'STD_20', 'Z_SCORE', 'SIGNAL_20']].iloc[20:30]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In addition, let's also prepare a backtest of a **\"baseline\"** in terms of a simple **buy-and-hold** trading strategy for comparison purposes. Our buy-and-hold strategy sends a \"long\" (+1.0) signal for each time step: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_mav_eurusd_data['SIGNAL_BASE'] = 1.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Prepare the trading signal data to be utilized in backtesting the mean-reversion trading strategy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# convert signals to Pandas DataFrame\n",
    "mre_mav_eurusd_signal_data = pd.DataFrame(mre_mav_eurusd_data[['SIGNAL_20', 'SIGNAL_BASE']], columns=['SIGNAL_20', 'SIGNAL_BASE'])\n",
    "\n",
    "# convert pandas DataFrame index to datatype: datetime\n",
    "mre_mav_eurusd_signal_data = mre_mav_eurusd_signal_data.set_index(pd.to_datetime(mre_mav_eurusd_signal_data.index))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inspect top 10 rows of the prepared trading signals:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_mav_eurusd_signal_data.head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inspect some of the exemplary signal deviations between the **mean-reversion** and our baseline **buy and hold** trading strategies:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_mav_eurusd_signal_data[mre_mav_eurusd_signal_data['SIGNAL_20'] != mre_mav_eurusd_signal_data['SIGNAL_BASE']].head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Visualize the prepared trading signals:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.rcParams['figure.figsize'] = [15, 5]\n",
    "fig, ax = plt.subplots(ncols=1, nrows=2)\n",
    "\n",
    "ax[0].plot(mre_mav_eurusd_signal_data['SIGNAL_20'], lw=1.0, color='C2', label='SMAV 16 (red)')\n",
    "ax[1].plot(mre_mav_eurusd_signal_data['SIGNAL_BASE'], lw=1.0, color='C3', label='BASE (purple)')\n",
    "    \n",
    "# set axis labels\n",
    "plt.xlabel('[time]', fontsize=10)\n",
    "ax[0].set_xlim([start_date, end_date])\n",
    "ax[0].set_ylabel('[mre 100 signal]', fontsize=10)\n",
    "ax[1].set_xlim([start_date, end_date])\n",
    "ax[1].set_ylabel('[base signal]', fontsize=10)\n",
    "\n",
    "# rotate the x-axis labels\n",
    "for tick in ax[0].get_xticklabels():\n",
    "    tick.set_rotation(45)\n",
    "    \n",
    "for tick in ax[1].get_xticklabels():\n",
    "    tick.set_rotation(45)\n",
    "\n",
    "# set plot title\n",
    "ax[0].set_title('Euro vs. US-Dollar Exchange Rate - Mean Reversion Trading Signals', fontsize=10)\n",
    "ax[1].set_title('Euro vs. US-Dollar Exchange Rate - Baseline Buy and Hold Trading Signals', fontsize=10)\n",
    "\n",
    "# reset plot layout\n",
    "plt.tight_layout()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's determine the total number of **long-short signal changes** of the distinct trading strategies:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# signal changes of the mean-reversion trading strategy\n",
    "len(list(it.groupby(mre_mav_eurusd_signal_data['SIGNAL_20'], lambda x: x > 0)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# signal changes of the baseline buy and hold trading strategy\n",
    "len(list(it.groupby(mre_mav_eurusd_signal_data['SIGNAL_BASE'], lambda x: x > 0)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Mean Reversion Signal Backtest"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Prepare the market data to be utilized in backtesting the mean reversion trading strategy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# extract the eurusd exchange rate closing prices\n",
    "eurusd_market_data = pd.DataFrame(eurusd_data['Adj Close'], columns=['Adj Close'])\n",
    "\n",
    "# rename the 'close' column to 'eurusd' (since this is the column we want to allocate to in the backtest)\n",
    "eurusd_market_data = eurusd_market_data.rename(columns={'Adj Close': 'EURUSD'})\n",
    "\n",
    "# convert pandas DataFrame index to datatype: datetime\n",
    "eurusd_market_data = eurusd_market_data.set_index(pd.to_datetime(eurusd_data.index))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inspect top 10 rows of the prepared market data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eurusd_market_data.head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Implementing a simple Mean Reversion Trading Strategy by interfacing the Python `bt`'s Algo class:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MeanReversionStrategy(bt.Algo):\n",
    "    \n",
    "    # inits the strategy\n",
    "    def __init__(self, signals):\n",
    "        \n",
    "        # set class signals\n",
    "        self.signals = signals\n",
    "        \n",
    "    # calss the trading strategy\n",
    "    def __call__(self, target):\n",
    "        \n",
    "        # case: current timestep in signals\n",
    "        if target.now in self.signals.index[1:]:\n",
    "            \n",
    "            # get actual signal\n",
    "            signal = self.signals[target.now]\n",
    "            \n",
    "            # set target weights according to signal\n",
    "            target.temp['weights'] = dict(EURUSD=signal)\n",
    "            \n",
    "        # return 'True' since we want to move on to the next timestep\n",
    "        return True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define the moving average trading strategy backtest algorithm stack. \n",
    "\n",
    "**Note:** That in the Python `bt` library a trading strategy usually consists of a so-called **stack of algorithms**. For each timestep of our backtest timeframe, the `bt` library executes all algorithm of the stack in sequential order. Each moving average strategy we aim to design and backtest consists in total of three algorithms, briefly described in the following: \n",
    "\n",
    "> 1. `bt.algos.SelectAll()`: Selects all available stocks for trading except stock prices that correspond to NaN or 0.00.  \n",
    "> 2. `MovingAverageStrategy()`: Assigns the calculated signal in terms of a weight value to the EUR vs. USD exchange rate.\n",
    "> 3. `bt.algos.Rebalance()`: Rebalances the available capital based on the weights assigned to each stock."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define the mean-reversion and buy-and-hold trading strategy backtest algorithm stack: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mre_mav_eurusd_strategy_20 = bt.Strategy(name='mre_20', algos=[bt.algos.SelectAll(), MeanReversionStrategy(mre_mav_eurusd_signal_data['SIGNAL_20']), bt.algos.Rebalance()])\n",
    "mre_mav_eurusd_strategy_base = bt.Strategy(name='base', algos=[bt.algos.SelectAll(), MeanReversionStrategy(mre_mav_eurusd_signal_data['SIGNAL_BASE']), bt.algos.Rebalance()])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's now define the trading ('fees') commissions used in each rebalancing time-step of a backtest. To achieve this, the `bt` library expects a callable function that expects the following two parameters as an input:\n",
    "\n",
    "> - the 'quantity', denoted by `q`, of rebalanced assets at a backtest time-step;\n",
    "> - the 'price', denoted by `p`, of rebalanced assets at a backtest time-step.\n",
    "\n",
    "Let's implement such a callable function defining a trading fee of **1\\% (0.01)** per quantity of rebalanced asset (or a flat fee of **USD 5.00** per trade):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# init trading fees function\n",
    "def trading_fees_function(q, p):\n",
    "    \n",
    "    # calcluate trading fees (rebalanced-quantity * trading-fee)\n",
    "    fees = 5.00 # flat fee of USD 5.00 per trade\n",
    "    \n",
    "    # return the total trading fees\n",
    "    return fees"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Upon completion of defining the mean-reversion strategy let's now init the corresponding backtests using (1) both strategies as well as (2) the market data that we aim to evaluate during the backtest:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eurusd_backtest_mre_mav_20 = bt.Backtest(strategy=mre_mav_eurusd_strategy_20, data=eurusd_market_data, name='eurusd_backtest_mre_20', commissions=trading_fees_function, progress_bar=True)\n",
    "eurusd_backtest_mre_mav_base = bt.Backtest(strategy=mre_mav_eurusd_strategy_base, data=eurusd_market_data, name='eurusd_backtest_mre_base', commissions=trading_fees_function, progress_bar=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, let's run the backtest of the mean-reversion trading strategy configuration as well as the defined baseline:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "backtest_results_eurusd = bt.run(eurusd_backtest_mre_mav_20, eurusd_backtest_mre_mav_base)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inspect the individual backtest results and performance measures:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "backtest_results_eurusd.display()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Collect detailed backtest performance per timestep of the **mean-reversion** strategy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "backtest_mre_20_eurusd_details = eurusd_backtest_mre_mav_20.strategy.prices.to_frame(name='Rel. EQUITY')\n",
    "backtest_mre_20_eurusd_details['Abs. EQUITY'] = eurusd_backtest_mre_mav_20.strategy.values # equity per timestep\n",
    "backtest_mre_20_eurusd_details['CASH'] = eurusd_backtest_mre_mav_20.strategy.cash # cash per timestep\n",
    "backtest_mre_20_eurusd_details['POSITIONS'] = eurusd_backtest_mre_mav_20.strategy.positions # positions per timestep\n",
    "backtest_mre_20_eurusd_details['FEES'] = eurusd_backtest_mre_mav_20.strategy.fees # trading fees per timestep"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inspect detailed backtest results per timestep:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "backtest_mre_20_eurusd_details.head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Visualize the monthly returns obtained by the **mean-reversion** trading strategy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.rcParams['figure.figsize'] = [15, 10]\n",
    "fig = plt.figure()\n",
    "ax = fig.add_subplot(111)\n",
    "\n",
    "# plot heatmap of monthly returns generated by the strategy\n",
    "ax = sns.heatmap(eurusd_backtest_mre_mav_20.stats.return_table, annot=True, cbar=True, vmin=-0.5, vmax=0.5)\n",
    "\n",
    "# set axis labels\n",
    "ax.set_xlabel('[month]', fontsize=10)\n",
    "ax.set_ylabel('[year]', fontsize=10)\n",
    "\n",
    "# set plot title\n",
    "ax.set_title('Euro vs. US-Dollar Exchange Rate - Monthly Returns Mean Reversion Trading Strategy', fontsize=10);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Collect detailed backtest performance per timestep of the \"long only\" baseline strategy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "backtest_mre_base_eurusd_details = eurusd_backtest_mre_mav_base.strategy.prices.to_frame(name='Rel. EQUITY')\n",
    "backtest_mre_base_eurusd_details['Abs. EQUITY'] = eurusd_backtest_mre_mav_base.strategy.values # equity per timestep\n",
    "backtest_mre_base_eurusd_details['CASH'] = eurusd_backtest_mre_mav_base.strategy.cash # cash per timestep\n",
    "backtest_mre_base_eurusd_details['POSITIONS'] = eurusd_backtest_mre_mav_base.strategy.positions # positions per timestep\n",
    "backtest_mre_base_eurusd_details['FEES'] = eurusd_backtest_mre_mav_base.strategy.fees # fees per timestep"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inspect detailed backtest results per timestep:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "backtest_mre_base_eurusd_details.head(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.rcParams['figure.figsize'] = [15, 10]\n",
    "fig = plt.figure()\n",
    "ax = fig.add_subplot(111)\n",
    "\n",
    "# plot heatmap of monthly returns generated by the strategy\n",
    "ax = sns.heatmap(eurusd_backtest_mre_mav_base.stats.return_table, annot=True, cbar=True, vmin=-0.5, vmax=0.5)\n",
    "\n",
    "# set axis labels\n",
    "ax.set_xlabel('[month]', fontsize=10)\n",
    "ax.set_ylabel('[year]', fontsize=10)\n",
    "\n",
    "# set plot title\n",
    "ax.set_title('Euro vs. US-Dollar Exchange Rate - Monthly Returns Buy and Hold Baseline', fontsize=10);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Visualize each strategie's backtest equity progression over time:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.rcParams['figure.figsize'] = [15, 5]\n",
    "fig = plt.figure()\n",
    "ax = fig.add_subplot(111)\n",
    "\n",
    "# plot equity progression of the distinct trading strategies\n",
    "ax.plot(backtest_mre_20_eurusd_details['Rel. EQUITY'], color='C2',lw=1.0, label='20-day mean-reversion strategy (red)')\n",
    "ax.plot(backtest_mre_base_eurusd_details['Rel. EQUITY'], color='C3',lw=1.0, label='Base buy-and-hold strategy (purple)')\n",
    "\n",
    "# rotate x-tick labels\n",
    "for tick in ax.get_xticklabels():\n",
    "    tick.set_rotation(45)\n",
    "    \n",
    "# set axis labels\n",
    "ax.set_xlabel('[time]', fontsize=10)\n",
    "ax.set_xlim([start_date, end_date])\n",
    "ax.set_ylabel('[rel. equity]', fontsize=10)\n",
    "\n",
    "# set plot legend\n",
    "plt.legend(loc=\"upper left\", numpoints=1, fancybox=True)\n",
    "\n",
    "# set plot title\n",
    "plt.title('Euro vs. US-Dollar Exchange Rate - Backtest % Equity Progression', fontsize=10);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lab Exercises:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We recommend you to try the following exercises as part of the lab:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**1. Evaluation of distinct daily moving average parameters.**\n",
    "\n",
    "> Evaluate the mean-reversion trading strategy using distinct moving average look-backs, e.g., 10 days, 30 days, 50 days, 200 days and 300 days. Compare the performance of the lookback parametrizations in terms of total-return, equity progression and yearly sharpe-ratio. Gain an intuition about the years in which the strategy didn't perform well and the potential reason for the poor performance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ***************************************************\n",
    "# INSERT YOUR CODE HERE\n",
    "# ***************************************************"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**2. Evaluation of distinct Z-Score threshold parameters.** \n",
    "\n",
    ">Evaluate the simple mean-reversion trading strategy using distinct Z-Score thresholds, e.g., $Z$ = 2, 3, 5, 10. Calculate the trading frequency of each parametrization and compare their performance in terms of total-return, equity progression and yearly-sharpe-ratio. Gain an intuition about the years in which the strategy didn't perform well and the potential reason of the poor performance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ***************************************************\n",
    "# INSERT YOUR CODE HERE\n",
    "# ***************************************************"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**3. Consideration of trading commissions and impact on strategy performance.** \n",
    "\n",
    ">Run the backtest of the lookback parametrizations evaluated in exercise 1. but change the commission per trade. Set the trading commission of each backtest to (i) 10 USD per trade and (ii) 1% of the price per share. Determine the impact of such a trading fee on the performance of each strategy in terms of total-return, equity progression and yearly sharpe-ratio."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ***************************************************\n",
    "# INSERT YOUR CODE HERE\n",
    "# ***************************************************"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**4. Optimization of the mean-reversion trading strategy parameters.**\n",
    "\n",
    ">The lab notebook backtest results obtained for the mean-reversion trading strategy reveals that we did identify a well-performing strategy parametrization. However, there is definitely a \"room for improvement\". Grid search the parameter space of the strategy to determine a parametrization that results in a positive total-return within the time interval 31.12.2003 until 31.12.2017."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ***************************************************\n",
    "# INSERT YOUR CODE HERE\n",
    "# ***************************************************"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lab Summary:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this lab, a step by step implementation and backtest of a basic **mean-reversion trading strategy** using the Python programming language is presented. The implemented strategy trades a specific foreign exchange rate based on its adjusted closing price trend. The degree of success of the implemented strategy is evaluated based os its backtest results with particular focus on (1) the strategy's **total return** as well as (2) its **equity progression** over time. The code provided in this lab provides a blueprint to develop and backtest more complex trading strategies. It furthermore can be tailored to be applied for momentum trading of other financial instruments."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You may want to execute the content of your lab outside of the Jupyter notebook environment, e.g. on a compute node or a server. The cell below converts the lab notebook into a standalone and executable python script. Pls. note that to convert the notebook, you need to install Python's **nbconvert** library and its extensions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# installing the nbconvert library\n",
    "!pip3 install nbconvert\n",
    "!pip3 install jupyter_contrib_nbextensions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's now convert the Jupyter notebook into a plain Python script:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!jupyter nbconvert --to script cfds_lab_04.ipynb"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
